<?php
/**
 * PHP调用webServer接口DEMO
 * 以调用天气预报为例
 * 
 * WS请求公共类
 * 同时支持WSDL和non-WSDL两种模式
 */
class WebServer
{
    //WS请求地址
    protected $url;

    //WS实例参数
	//这里参数可以根据实际需求添加，如加密令牌、ssl、http等参数
    protected $options;

    //WS头参数
    protected $headers;

    protected $client;

    public function __construct($url = null, $options = [], $headers = []) {
        $this->url     = $url;
        $this->options = $options;
        $this->headers = $headers;

        $this->createSoapClient();
    }

    public function createSoapClient(){
        //non-WSDL模式参数location(发送请求的SOAP服务器的URL)和uri(SOAP服务的目标名称空间)为必填
        if (!$this->url && (!$this->options['location'] || !$this->options['uri'])) {
			return false;
		}

        //设置代理请求WS
        // $options = [
        //     'proxy_host'     => '代理地址',              //不需要添加http或https
        //     'proxy_port'     => '代理端口',
        //     'proxy_login'    => '代理账号',
        //     'proxy_password' => '代理密码'
        // ];

        try {
            $this->client = new SoapClient($this->url, $this->options);
            if ($this->client) {
                return $this->client;
            } else {
                return false;
            }
        } catch (\SoapFault $e){
            file_put_contents('./log.txt', $e->getMessage());
            return false;
        } catch (\Exception $e) {
            file_put_contents('./log.txt', $e->getMessage());
            return false;
        }
    }

    /**
     * 设置WS头
     */
    public function setSoapHeaders(){
        if (count($this->headers)) {
            $this->client->__setSoapHeaders($this->headers);
        }
    }

    /**
     * 获取WS中可以使用的方法和参数
	 * 在调用前，可以使用getFunctions和getTypes看一下该接口暴露的方法、参数和数据类型
	 * 这里需要注意的是传入的参数名一定要和soapclient里面定义的一致，否则参数是传不过去的
     */
    public function getWsFunAndType(){
		if(!$this->client)  return false;
		
        $functions = $this->client->__getFunctions();
        $types = $this->client->__getTypes();

        return ['functions' => $functions, 'types' => $types];
    }

    /**
     * 请求WS
     *
     * @param string       $method 待请求WS方法
     * @param array|string $data   待发送数据，['parm1'=>'参数1', 'parm2'=>'参数2', 'parm3'=>'参数3', ……]
     * @return boolean 是否成功
     */
    public function callSoapFun($method, $data)
    {
        if (!$method)  return false;
        $this->setSoapHeaders();

        try{
            $responseRes = $this->client->__soapCall($method, [$data]);
        } catch (\SoapFault $e){
            return false;
        } catch (\Exception $e) {
            return false;
        } catch (\Throwable $e) {
            //Throwable 在 PHP 7 中可以用作任何对象抛出声明的基本接口，包括 Expection （异常）和 Error （错误）
            return false;
        }

        if (is_object($responseRes)) {
            $responseRes = $this->objectToArray($responseRes);
        }

        return $responseRes ? $responseRes : false;
    }

    /**
     * 将stdClass Object转换成array格式
     * @param  $array 需要转换的对象
     * @return array
     */
    public function objectToArray($array) {
        if(is_object($array)) {
            $array = (array)$array;
        } if(is_array($array)) {
            foreach($array as $key=>$value) {
                $array[$key] = $this->objectToArray($value);
            }
        }
        return $array;
    }
}

$wsdl = 'http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl';
$client = new WebServer($wsdl, ['encoding'=>'UTF-8']);
$result = $client->callSoapFun('getWeatherbyCityName', ['theCityName'=>'天津']);
print_r($result);
